/**
*\file utils.c
*\author H4201
*/

#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../interface/messqueue.h"
#include <pthread.h>

/*EXPORTED FUNCTIONS*/
/**
	Crée une message queue nommée un_nom.
	Si une message queue avec le même nom existe déjà, alors elle est supprimée et on en crée une nouvelle
	Le descripteur ne donne aucun droit en lecture écriture sur le message queue
	*\param un_nom nom de la message queue. Doit commencer par un /	
	*\param nb_max_messages est le nombre max de message que la messageQueue peut accepter
	*\return un descripteur vers la message queue crée
*/
mqd_t creer_mqueue(char * un_nom, int nb_max_messages) {
	struct mq_attr mqArch;
	mqArch.mq_maxmsg = nb_max_messages;
	mqArch.mq_msgsize = sizeof(struct message);
	mqArch.mq_flags = 0;
	mqArch.mq_curmsgs = 0;

	mqd_t message_queue;
	if ((message_queue = mq_open(un_nom, O_CREAT | O_EXCL, 0644, mqArch)) == -1) {
		printf("detruit BM déjà existante\n");
		//tentive de destruction puis de re-creation
		if ( (mq_unlink(un_nom) == -1) ||
			((message_queue = mq_open(un_nom, O_CREAT | O_EXCL, 0644, mqArch)) == -1) ) {
			perror("erreur creation mqueue");
			exit(1);
		}
	}

	return message_queue;
}

/**
	Ouvre une message queue nommée un_nom.
 	La message queue doit exister.
	*\param un_nom nom de la message queue. Doit commencer par un /
	*\param oflag droit du descripteur mode d'ouverture (lecture, écriture). Voir O_RDONLY, O_WRONLY, O_RDWR
	*\return un descripteur vers la message queue ouverte
*/
mqd_t ouvrir_mqueue(char * un_nom, int oflag) {
	mqd_t message_queue;
	if ( (message_queue = mq_open(un_nom, oflag)) == -1) {
		printf("%s\n", un_nom);
		perror("Erreur ouverture mq_open");
		exit(1);
	}
	return message_queue;
}

/**
	lis un message dans un_message queue
	l'appel est bloquant à condition de ne pas avoir donné de flag O_NONBLOCK
	à la message queue
	*\param un_message_queue: le descripteur de la message queue ouvert en lecture
	*\param msg: un pointeur vers une struct message pour stocker le message reçu
	*\return -1 si erreur, taille lue sinon
*/
int lire_message(mqd_t un_message_queue, message_t * msg) {
	int taille_lue;
	struct mq_attr attr;

	mq_getattr(un_message_queue, &attr);

	char buffer[attr.mq_msgsize];
	if ( ( taille_lue = mq_receive(un_message_queue, buffer, sizeof(buffer), NULL)) == -1) {
		//perror("erreur lecture mqueue");
		return taille_lue;
	} 
	memcpy(msg, buffer, taille_lue);

	return taille_lue;
}

/**
	Crée un thread dit temps réel, avec la priorité donnée en paramètre.
	Ordonnancement de type FIFO, nécessite les droits root.
	*\param t handler de thread à utiliser
	*\param start_routine fonction à exécuter dans le thread
	*\param arg argument pour cette fonction
	*\param prio priorité du thread
	*\return 0 si le lancement du thread s'est bien passé, un code erreur sinon
*/
int creer_thread_tr(pthread_t * t, void *(*start_routine) (void *), void * arg, int prio) {
	int err;

	pthread_attr_t attr_t;
	struct sched_param param;

	pthread_attr_init (& attr_t);

	if ((err = pthread_attr_setschedpolicy(& attr_t, SCHED_FIFO)) != 0) {
		//perror("setschedpolicy");
		return err;
	}
	if ((err = pthread_attr_setinheritsched(& attr_t, PTHREAD_EXPLICIT_SCHED)) != 0) {
		//perror("setinheritsched");
		return err;
	}
	//TODO: magic number à remplacer par une constante partagée
	param.sched_priority  = prio;
	if ((err = pthread_attr_setschedparam(& attr_t, & param)) != 0) {
		//perror("setschedparam");
		return err;
	}
	if ((err = pthread_create(t, & attr_t, start_routine, arg)) != 0) {
		//perror("pthread_create");
		return err;
	}

	return err;
}

/**
	Vide la boite aux lettres donnée en paramètre
	*\param bal descripteur de la boite au lettre à vider
*/
void vider_bal(mqd_t bal) {
	struct mq_attr attr;
	struct message msg_osef;
	int i;

	mq_getattr(bal, &attr);
	for(i=0; i<attr.mq_curmsgs;i++) {
		lire_message(bal, &msg_osef);
	}
}
